《Day 01:為什麼是 Filament,跟手刻後台與其他套件比起來差在哪》 結尾留下一句話,決策依據講得再清楚,終究只是紙上談兵,真正要驗證的,是把 Filament 裝進示範專案裡,親手看看第一個 Panel 長什麼樣子。今天要做的正是這件事。
今天的任務範圍要先畫清楚。今天只做兩件事,完成 Filament 的安裝,並且建立第一個 Panel。Resource,也就是實際的商品管理介面,還沒有輪到今天登場,那是 《Day 03:建立第一個 Resource,五分鐘做出一套 CRUD 介面》 的任務。今天寧可把 Panel 這個概念講到你真正理解為止,也不要急著往下衝去做出一個看起來有內容的畫面,卻不知道剛才那個指令背後到底幫你生成了什麼。
這個順序不是隨意安排的。Panel 是後續所有 Resource、Widget 存在的容器,如果今天含糊帶過,跳去做 Resource,你會發現自己接下來每建立一個新功能,都得回頭查一次「這個東西應該放進哪個 Panel」,因為你從一開始就沒有真正理解這個容器在做什麼。今天把地基打好,後面 28 天才不會一直卡在同一個問題上。
Filament 不是一個獨立的應用程式,而是安裝進既有 Laravel 專案裡的套件,這件事延續 Day 01 提過的定位,它深度綁定 Laravel 既有的機制,所以第一步永遠是先有一個能跑起來的 Laravel 專案。如果你是跟著這個系列從零開始,建立寵物用品批發商專案的後台,先用 laravel new PetHub 建立一個新專案,確認 .env 裡的資料庫連線設定正確,能跑過 php artisan migrate 和 php artisan test 不報錯,這是今天所有動作的前提。
確認專案沒問題之後,安裝 Filament 本身:
composer require filament/filament:"^5.0"
安裝完成後,跑官方提供的安裝指令:
php artisan filament:install --panels
這道指令會做幾件事,發布 Filament 需要的設定檔與資源檔,並且在你的專案裡建立第一個 Panel 的骨架,預設路徑通常是 app/Providers/Filament/AdminPanelProvider.php。這個檔名裡的 Admin 不是巧合,它代表這是官方預設幫你生成的第一個 Panel,命名為管理者後台,這正是今天要打開來看清楚的檔案。
[!tip] 為什麼是 Service Provider,不是 Controller 或 Route
如果你熟悉 Laravel 的服務容器與服務提供者機制,看到 Panel 是以一個 Service Provider 的形式存在,應該不會太意外。Panel 要做的事情,是在應用程式啟動階段就把自己註冊進 Laravel 的服務容器裡,決定這個後台入口要用什麼樣的路徑、什麼樣的中介層、載入哪些資源,這些都屬於應用程式啟動時期就該確定的組態設定,而不是某一次請求進來才臨時決定的事,這正是 Service Provider 存在的典型場景,跟你替專案註冊其他服務的方式完全一致。
打開 AdminPanelProvider.php,會看到一個鏈式呼叫組成的設定區塊,大致長這樣:
public function panel(Panel $panel): Panel
{
return $panel
->default()
->id('admin')
->path('admin')
->login()
->colors([
'primary' => Color::Amber,
])
->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
->middleware([
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartSession::class,
AuthenticateSession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
SubstituteBindings::class,
DisableBladeIconComponents::class,
DispatchServingFilamentEvent::class,
])
->authMiddleware([
Authenticate::class,
]);
}
先不要被這一長串鏈式呼叫嚇到,這裡逐項對照今天要建立的錨點定義,Panel 是後台入口的最上層容器,決定這個入口底下有哪些 Resource、Widget、主題與存取範圍。這份設定檔裡的每一行,其實都在回答這四件事的其中一項。
id('admin') 與 path('admin') 決定了這個入口本身,id 是這個 Panel 在程式碼裡的識別名稱,path 決定使用者要打開瀏覽器輸入什麼網址才能進到這個入口,預設是你的網域後面接 /admin。colors 決定這個入口的主題外觀。discoverResources、discoverPages、discoverWidgets 這三行,決定了 Filament 會自動掃描哪些資料夾,把裡面找到的類別當作這個 Panel 底下要顯示的 Resource、自訂頁面與 Widget,這正是「決定底下有哪些 Resource、Widget」這句話具體對應的程式碼。middleware 與 authMiddleware 這兩塊,決定了誰有資格進到這個入口,也就是「存取範圍」對應的部分。
[!warning]
discoverResources掃描的是資料夾,不是你手動註冊的清單
這裡有一個容易搞混的地方,跟你熟悉的 Laravel 路由註冊方式不太一樣。一般 Laravel 路由通常需要你在routes檔案裡明確寫下每一條路由,但 Filament 的 Resource 預設不需要你手動列出清單,只要這個類別檔案被放進discoverResources指定的資料夾裡,並且符合命名規則,Panel 啟動時就會自動把它掃描進來。這代表明天建立 Resource 時,你不需要回來這個檔案手動註冊新增的 Resource,只要檔案放對資料夾,它就會自動出現在這個 Panel 裡。
今天的錨點定義裡有一個詞值得特別停下來看,「一個獨立後台入口」,這句話背後藏著一個很重要的延伸事實,一個 Laravel 專案裡,可以同時存在不只一個 Panel。
具體想像一下,寵物用品批發商的後台,未來除了管理者使用的入口之外,如果要另外開放一個給合作的寵物用品店老闆自己登入查詢訂單進度的入口,這兩種使用者的權限範圍、看到的功能、甚至主題外觀都截然不同,這時候合理的做法不是把兩種角色硬塞進同一個 Panel 裡用權限判斷區隔,而是建立第二個 Panel,各自擁有獨立的 id、path、discoverResources 設定範圍。這個延伸情境今天不會實際動手做,先記住這個可能性即可,等系列後面談到多租戶隔離時,會再回頭討論 Panel 之間的邊界劃分。
今天安裝指令幫你生成的這個 admin,就是寵物用品批發商後台系列全程會使用的唯一入口,管理者、倉管、業務、財務,這些角色未來都會登入同一個 Panel,差異透過權限機制區分,而不是拆成多個 Panel,這個設計決定會在系列後面談角色權限時說明理由。
設定檔看懂之後,回到終端機,確認資料庫遷移都已跑過,並建立一個可以登入的使用者帳號:
php artisan make:filament-user
依照提示輸入姓名、電子郵件、密碼,建立完成後,啟動開發伺服器,打開瀏覽器輸入 你的網域/admin,會看到 Filament 提供的登入畫面。輸入剛才建立的帳號密碼,登入後會看到一個目前還是空的後台首頁,因為還沒有任何 Resource 或 Widget 存在,但這個空殼本身,已經是一個真正掛載到 Laravel 服務容器裡、有自己的路徑、有自己的認證機制、正在運作中的 Panel。
今天完成了兩件事,把 Filament 安裝進示範專案,並且看懂了 AdminPanelProvider.php 這個檔案裡每一行設定分別對應 Panel 容器概念的哪個面向,入口識別與路徑、外觀主題、內容範圍、存取範圍。也確認了一件事,一個 Laravel 專案可以有不只一個 Panel,只是這個系列全程只會使用今天建立的這一個。
但今天打開瀏覽器看到的,終究只是一個空殼,登入畫面能用,首頁卻什麼都沒有。這個容器已經準備好了,明天要開始往裡面放進第一件真正有內容的東西,寵物用品批發商的商品管理介面。